# 导入必要的库
import pandas as pd  # 数据处理库
import numpy as np  # 数值计算库
import matplotlib.pyplot as plt  # 绘图库
import warnings  # 警告处理
import micropip  # 包管理工具
import seaborn as sns  # 数据可视化库
from sklearn.linear_model import Ridge  # 岭回归模型
from sklearn.model_selection import train_test_split  # 数据集分割
from sklearn.model_selection import cross_val_predict  # 交叉验证预测
import ipywidgets  # 交互式控件
from sklearn.preprocessing import PolynomialFeatures  # 多项式特征
from sklearn.model_selection import GridSearchCV  # 网格搜索
from ipywidgets import interact, interactive, fixed, interact_manual  # 交互式控件函数

# 忽略警告信息
warnings.filterwarnings('ignore')

# 数据文件路径
filepath = 'https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-DA0101EN-SkillsNetwork/labs/Data%20files/module_5_auto.csv'

# 读取CSV文件数据
df = pd.read_csv(filepath)

# 显示数据前5行
print(df.head())

# 只保留数值型数据
df=df._get_numeric_data()

# 删除不需要的列
df.drop(['Unnamed: 0.1', 'Unnamed: 0'], axis=1, inplace=True)

# 显示处理后的数据前5行
print(df.head())

# 定义分布图绘制函数
def DistributionPlot(RedFunction, BlueFunction, RedName, BlueName, Title):
    # 设置图形大小
    width = 12
    height = 10
    plt.figure(figsize=(width, height))
    
    # 绘制红色分布曲线
    ax1 = sns.kdeplot(RedFunction, color="r", label=RedName)
    # 绘制蓝色分布曲线
    ax2 = sns.kdeplot(BlueFunction, color="b", label=BlueName, ax=ax1)

    # 设置标题和坐标轴标签
    plt.title(Title)
    plt.xlabel('Price (in dollars)')
    plt.ylabel('Proportion of Cars')
    plt.show()
    plt.close()

# 定义多项式回归图绘制函数
def PollyPlot(xtrain, xtest, y_train, y_test, lr,poly_transform):
    # 设置图形大小
    width = 12
    height = 10
    plt.figure(figsize=(width, height))
    
    # 计算x轴范围
    xmax=max([xtrain.values.max(), xtest.values.max()])
    xmin=min([xtrain.values.min(), xtest.values.min()])
    
    # 生成x轴数据点
    x=np.arange(xmin, xmax, 0.1)

    # 绘制训练数据点（红色）
    plt.plot(xtrain, y_train, 'ro', label='Training Data')
    # 绘制测试数据点（绿色）
    plt.plot(xtest, y_test, 'go', label='Test Data')
    # 绘制预测函数曲线
    plt.plot(x, lr.predict(poly_transform.fit_transform(x.reshape(-1, 1))), label='Predicted Function')
    
    # 设置y轴范围
    plt.ylim([-10000, 60000])
    plt.ylabel('Price')
    plt.legend()

# 定义目标变量（价格）
y_data = df['price']

# 定义特征变量（除价格外的所有列）
x_data = df.drop('price', axis=1)

# 分割数据集：90%训练，10%测试
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.10, random_state=1)

# 打印测试集和训练集样本数量
print("number of test samples :", x_test.shape[0])
print("number of training samples:",x_train.shape[0])

# 另一种分割方式：60%训练，40%测试
x_train1, x_test1, y_train1, y_test1 = train_test_split(x_data, y_data, test_size=0.4, random_state=0) 
print("number of test samples :", x_test1.shape[0])
print("number of training samples:",x_train1.shape[0])

# 导入线性回归模型
from sklearn.linear_model import LinearRegression

# 创建线性回归对象
lre=LinearRegression()

# 使用马力特征训练模型
lre.fit(x_train[['horsepower']], y_train)

# 打印训练集和测试集的R²分数
print("R^2 score for training set:", lre.score(x_train[['horsepower']], y_train))
print("R^2 score for test set:", lre.score(x_test[['horsepower']], y_test))

# 导入交叉验证分数计算
from sklearn.model_selection import cross_val_score

# 4折交叉验证
Rcross = cross_val_score(lre, x_data[['horsepower']], y_data, cv=4)
print("The mean of the folds are", Rcross.mean(), "and the standard deviation is" , Rcross.std())

# 计算负均方误差
print(-1 * cross_val_score(lre,x_data[['horsepower']], y_data,cv=4,scoring='neg_mean_squared_error'))

# 2折交叉验证
Rc=cross_val_score(lre,x_data[['horsepower']], y_data,cv=2)
print(Rc.mean())

# 使用交叉验证进行预测
yhat = cross_val_predict(lre,x_data[['horsepower']], y_data,cv=4)
print(yhat[0:5])

# 创建新的线性回归模型
lr = LinearRegression()

# 使用多个特征训练模型
lr.fit(x_train[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']], y_train)

# 对训练集和测试集进行预测
yhat_train = lr.predict(x_train[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']])
yhat_test = lr.predict(x_test[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']])

# 打印前5个预测值
print("Predicted values:", yhat_train[0:5])

# 绘制训练数据的分布图
Title = 'Distribution  Plot of  Predicted Value Using Training Data vs Training Data Distribution'
DistributionPlot(y_train, yhat_train, "Actual Values (Train)", "Predicted Values (Train)", Title)

# 绘制测试数据的分布图
Title='Distribution  Plot of  Predicted Value Using Test Data vs Data Distribution of Test Data'
DistributionPlot(y_test,yhat_test,"Actual Values (Test)","Predicted Values (Test)",Title)

# 重新分割数据集：55%训练，45%测试
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.45, random_state=0)

# 创建5次多项式特征
pr = PolynomialFeatures(degree=5)
x_train_pr = pr.fit_transform(x_train[['horsepower']])
x_test_pr = pr.fit_transform(x_test[['horsepower']])

# 创建多项式回归模型
poly = LinearRegression()
poly.fit(x_train_pr, y_train)

# 进行预测
yhat = poly.predict(x_test_pr)

# 打印预测值和真实值
print("Predicted values:", yhat[0:4])
print("True values:", y_test[0:4].values)

# 绘制多项式回归图
PollyPlot(x_train['horsepower'], x_test['horsepower'], y_train, y_test, poly,pr)

# 打印训练集和测试集的R²分数
print(poly.score(x_train_pr, y_train))
print(poly.score(x_test_pr, y_test))

# 测试不同多项式阶数的R²分数
Rsqu_test = []

# 定义多项式阶数
order = [1, 2, 3, 4]

# 遍历不同阶数
for n in order:
    # 创建多项式特征
    pr = PolynomialFeatures(degree=n)
    
    # 转换训练集和测试集特征
    x_train_pr = pr.fit_transform(x_train[['horsepower']])
    x_test_pr = pr.fit_transform(x_test[['horsepower']])    
    
    # 训练线性回归模型
    lr.fit(x_train_pr, y_train)
    
    # 记录测试集R²分数
    Rsqu_test.append(lr.score(x_test_pr, y_test))

# 绘制R²分数随多项式阶数变化的图表
plt.plot(order, Rsqu_test)
plt.xlabel('order')
plt.ylabel('R^2')
plt.title('R^2 Using Test Data')
plt.text(3, 0.75, 'Maximum R^2 ')    

# 定义交互式函数
def f(order, test_data):
    # 根据测试数据比例分割数据集
    x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=test_data, random_state=0)
    
    # 创建多项式特征
    pr = PolynomialFeatures(degree=order)
    x_train_pr = pr.fit_transform(x_train[['horsepower']])
    x_test_pr = pr.fit_transform(x_test[['horsepower']])
    
    # 创建并训练多项式回归模型
    poly = LinearRegression()
    poly.fit(x_train_pr,y_train)
    
    # 绘制多项式回归图
    PollyPlot(x_train['horsepower'], x_test['horsepower'], y_train, y_test, poly,pr)

# 创建2次多项式特征（多个特征）
pr1=PolynomialFeatures(degree=2)
x_train_pr1=pr1.fit_transform(x_train[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']])
x_test_pr1=pr1.fit_transform(x_test[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']])

# 打印特征形状
print(x_train_pr1.shape)

# 创建并训练多项式回归模型
poly1=LinearRegression().fit(x_train_pr1,y_train)

# 进行预测
yhat_test1=poly1.predict(x_test_pr1)

# 绘制分布图
Title='Distribution  Plot of  Predicted Value Using Test Data vs Data Distribution of Test Data'
DistributionPlot(y_test, yhat_test1, "Actual Values (Test)", "Predicted Values (Test)", Title)

# 创建2次多项式特征（更多特征）
pr=PolynomialFeatures(degree=2)
x_train_pr=pr.fit_transform(x_train[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg','normalized-losses','symboling']])
x_test_pr=pr.fit_transform(x_test[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg','normalized-losses','symboling']])

# 创建岭回归模型（alpha=1）
RigeModel=Ridge(alpha=1)
RigeModel.fit(x_train_pr, y_train)

# 进行预测
yhat = RigeModel.predict(x_test_pr)

# 打印预测值和真实值
print('predicted:', yhat[0:4])
print('test set :', y_test[0:4].values)

# 导入进度条库
from tqdm import tqdm

# 初始化列表存储结果
Rsqu_test = []
Rsqu_train = []
dummy1 = []

# 定义alpha参数范围
Alpha = 10 * np.array(range(0,1000))

# 创建进度条
pbar = tqdm(Alpha)

# 遍历不同的alpha值
for alpha in pbar:
    # 创建岭回归模型
    RigeModel = Ridge(alpha=alpha) 
    RigeModel.fit(x_train_pr, y_train)
    
    # 计算测试集和训练集分数
    test_score, train_score = RigeModel.score(x_test_pr, y_test), RigeModel.score(x_train_pr, y_train)
    
    # 更新进度条显示
    pbar.set_postfix({"Test Score": test_score, "Train Score": train_score})

    # 记录分数
    Rsqu_test.append(test_score)
    Rsqu_train.append(train_score)

# 设置图形大小
width = 12
height = 10
plt.figure(figsize=(width, height))

# 绘制验证数据和训练数据的R²分数随alpha变化图
plt.plot(Alpha,Rsqu_test, label='validation data  ')
plt.plot(Alpha,Rsqu_train, 'r', label='training Data ')
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.legend()

# 使用alpha=10的岭回归模型
RigeModel = Ridge(alpha=10) 
RigeModel.fit(x_train_pr, y_train)

# 打印测试集分数
print(RigeModel.score(x_test_pr, y_test))

# 定义参数网格
parameters1= [{'alpha': [0.001,0.1,1, 10, 100, 1000, 10000, 100000, 100000]}]

# 创建岭回归对象
RR=Ridge()

# 创建网格搜索对象（4折交叉验证）
Grid1 = GridSearchCV(RR, parameters1,cv=4)

# 执行网格搜索
Grid1.fit(x_data[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']], y_data)

# 获取最佳模型
BestRR=Grid1.best_estimator_

# 打印最佳模型在测试集上的分数
print(BestRR.score(x_test[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']], y_test))

# 导入标准化器
from sklearn.preprocessing import StandardScaler

# 创建标准化器
scaler = StandardScaler()

# 标准化特征数据
x_scaled = scaler.fit_transform(x_data[['horsepower', 'curb-weight', 'engine-size', 'highway-mpg']])

# 定义参数网格（不包含'normalize'参数）
parameters2 = [{'alpha': [0.001, 0.1, 1, 10, 100, 1000, 10000, 100000, 1000000]}]

# 执行网格搜索
Grid2 = GridSearchCV(Ridge(), parameters2, cv=4)
Grid2.fit(x_scaled, y_data)

# 获取最佳模型
best_ridge_model = Grid2.best_estimator_

# 获取最佳alpha参数
best_alpha = Grid2.best_params_['alpha']
